﻿module Wpf.Mvc.ModelTests

open Xunit
open System.ComponentModel
open System.Windows.Controls
open System.Windows.Data
open System
open System.Collections.ObjectModel
open Swensen.Unquote.Assertions

[<AbstractClass>]
type Person() =
    inherit Model()

    abstract FirstName : string with get, set
    abstract LastName : string with get, set
    [<NotifyDependencyChanged>]
    member this.FullName = this.FirstName + " " + this.LastName

    abstract DOB : DateTime with get, set
    [<NotifyDependencyChanged>]
    member this.Age = 
        let timeElapsed = DateTime.Now - this.DOB
        let daysSinceBirthday = int timeElapsed.TotalDays
        daysSinceBirthday / 365

[<Fact>]
let DependentProperty() = 
    let model : Person = Model.Create()
    model.FirstName <- "Peter"
    model.LastName <- "Griffin"

    let textBox = TextBox(DataContext = model)
    let binding = Binding(path = "FullName", Mode = BindingMode.OneWay)
    textBox.SetBinding(TextBox.TextProperty, binding) |> ignore
    test <@ "Peter Griffin" = textBox.Text @>
    model.FirstName <- "Stewie"
    test <@ "Stewie Griffin" = textBox.Text @>


[<Fact>]
let UniqueBindings() = 
    let model : Person = Model.Create()
    model.FirstName <- "Peter"
    model.LastName <- "Griffin"

    let textBox = TextBox(DataContext = model)
    let binding = Binding(path = "FullName", Mode = BindingMode.OneWay)
    textBox.SetBinding(TextBox.TextProperty, binding) |> ignore
    let bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty)
    ()

[<Fact>]
let OneLevelLetExpansion() = 
    let model : Person = Model.Create()
    let textBox = TextBox(DataContext = model)
    let binding = Binding(path = "Age", Mode = BindingMode.OneWay)
    textBox.SetBinding(TextBox.TextProperty, binding) |> ignore
    model.DOB <- DateTime(year = 1974, month = 8, day = 23)
    test <@ "37" = textBox.Text @>

[<AbstractClass>]
type LineItem() =
    inherit Model()

    abstract Product : string with get, set
    abstract Price : decimal with get, set
    abstract Quantity : int with get, set

    [<NotifyDependencyChanged>]
    member this.Amount = this.Price * decimal this.Quantity

[<AbstractClass>]
type ShoppingCart() = 
    inherit Model()

    [<NotifyCollectionChanged>]
    abstract LineItems : LineItem ObservableCollection with get, set

    [<NotifyDependencyChanged>]
    member this.TotalAmount = if this.LineItems = null then 0M else this.LineItems |> Seq.sumBy(fun x -> x.Amount) 

[<Fact>]
let NotifyOnCollectionChanged() = 
    let model : ShoppingCart = Model.Create()
    model.LineItems <- ObservableCollection []
    let textBox = TextBox(DataContext = model)
    let binding = Binding(path = "TotalAmount", Mode = BindingMode.OneWay)
    textBox.SetBinding(TextBox.TextProperty, binding) |> ignore

    let lineItems = [
        "Wilson Tennis Racquets", 180M, 1
        "Penn Championship XD Tennis", 2M, 12
        "Vibration Dampener", 5M, 2
    ] 

    let mutable sum = 0M
    for product, price, quanity in lineItems do
        let li : LineItem = Model.Create()
        li.Product <- product
        li.Price <- price
        li.Quantity <- quanity
        model.LineItems.Add li
        sum <- sum + li.Amount
        test <@ string sum = textBox.Text @>

[<Fact>]
let StringFormatSingleBinding() = 
    let model = DateTime.Now
    let format = "{0}"
    match <@ String.Format(format, model.Year) @> with
    | BindingExpression binding -> 
        test <@ binding.Path.Path = "Year" @>
        test <@ binding.StringFormat = format @> 

//[<Fact>]
//let StringFormat2MultiBinding() = 
//    let model = DateTime.Now
//    let format = "{0} {1}"
//    match <@ String.Format(format, model.Year, model.Month) @> with
//    | BindingExpression(:? MultiBinding as binding) -> 
//        test <@ binding.Bindings.Count = 2 @>
//        test <@ binding.StringFormat = format @> 
//        test <@ (binding.Bindings.[0] :?> Binding).Path.Path = "Year" @>
//        test <@ (binding.Bindings.[1] :?> Binding).Path.Path = "Month" @>
//    | _ -> Assert.True false
//
//[<Fact>]
//let StringFormat3MultiBinding() = 
//    let model = DateTime.Now
//    let format = "{0} {1} {2}"
//    match <@ String.Format(format, model.Year, model.Month, model.Day) @> with
//    | BindingExpression(:? MultiBinding as binding) -> 
//        test <@ binding.Bindings.Count = 3 @>
//        test <@ binding.StringFormat = format @> 
//        test <@ (binding.Bindings.[0] :?> Binding).Path.Path = "Year" @>
//        test <@ (binding.Bindings.[1] :?> Binding).Path.Path = "Month" @>
//        test <@ (binding.Bindings.[2] :?> Binding).Path.Path = "Day" @>
//    | _ -> Assert.True false
//
//[<Fact>]
//let StringFormat4MultiBinding() = 
//    let model = DateTime.Now
//    let format = "{0} {1} {2} {3}"
//    match <@ String.Format(format, model.Year, model.Month, model.Day, model.Hour) @> with
//    | BindingExpression(:? MultiBinding as binding) -> 
//        test <@ binding.Bindings.Count = 4 @>
//        test <@ binding.StringFormat = format @> 
//        test <@ (binding.Bindings.[0] :?> Binding).Path.Path = "Year" @>
//        test <@ (binding.Bindings.[1] :?> Binding).Path.Path = "Month" @>
//        test <@ (binding.Bindings.[2] :?> Binding).Path.Path = "Day" @>
//        test <@ (binding.Bindings.[3] :?> Binding).Path.Path = "Hour" @>
//    | _ -> Assert.True false
